package rpc

import (
	"encoding/binary"
	"fmt"
	"io"
	"log"
	"net"
	"strings"
)

// Session 会话连接的结构体
type Session struct {
	Conn       net.Conn
	remoteAddr interface{}
	// 用于客户端请求后连接的归还标记
	destroyed bool
}

// NewSession 创建新连接
func NewSession(conn net.Conn) *Session {
	return &Session{Conn: conn}
}

// 向连接中写数据
func (s *Session) Write(data []byte) error {
	// 4字节头+数据长度切片
	buf := make([]byte, 4+len(data))
	//  写入头部数据，记录数据长度, binary 只认固定长度的类型，所以使用了uint32，而不是直接写入
	binary.BigEndian.PutUint32(buf[:4], uint32(len(data)))
	// fix: copy(buf[:4], data)
	copy(buf[4:], data)
	_, err := s.Conn.Write(buf)
	if err != nil {
		s.destroyed = true
		log.Println("write info fail, ", err)
		return err
	}
	return nil
}

// 从连接中读数据
func (s *Session) Read() ([]byte, error) {
	// 读取头部长度
	header := make([]byte, 4)
	// 按头部长度， 读取头部数据
	_, err := io.ReadFull(s.Conn, header)
	//_, err := s.conn.Read(header)
	if err != nil {
		if err.Error() == "EOF" || strings.Contains(err.Error(), "reset by peer") { // ignore
			s.destroyed = true
			return nil, err
		}
		fmt.Println("read rpc data header fail, ", string(header), err)
		return nil, err
	}
	// 读取数据长度 (大小端实现)
	dataLen := binary.BigEndian.Uint32(header)
	// 按照数据长度去读取数据
	data := make([]byte, dataLen)
	_, err = io.ReadFull(s.Conn, data)
	if err != nil {
		fmt.Println("read rpc data body fail, ", err)
		return nil, err
	}
	return data, nil
}

func (s *Session) Close() error {
	s.destroyed = true
	err := s.Conn.Close()
	if err != nil {
		log.Printf("session close err:%v, remote addr:%v", err, s.remoteAddr)
		return err
	} else {
		log.Printf("session closed, remote addr:%s", s.remoteAddr)
		return nil
	}
}
